/**
 * 用来测试条件变量； 注意条件变量消费者的使用，多个消费者的逻辑；
 * @author: sundx
 * @date: 2020-05-27 21:10:10
 * @Last Modified by: sundx
 * @Last Modified time: 2020-07-16 16:33:17
 */

#include <iostream>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <vector>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condVari = PTHREAD_COND_INITIALIZER;
std::vector<int> head;

void *producer(void *argv);
void *consumer(void *argv);

int main() {
  int ret;
  pthread_t tid[8];
  int i;
  for (i = 0; i < 4; ++i) { //生产者 3个
    ret = pthread_create(&tid[i], nullptr, producer, (void *)i);

    if (ret < 0) {
      std::cout << "pthread_create failed " << strerror(ret) << std::endl;
    }
  }
  for (; i < 8; ++i) { //消费者 5个
    // std::cout << i << std::endl;

    ret = pthread_create(&tid[i], nullptr, consumer, (void *)i);
    if (ret < 0) {
      std::cout << "pthread_create failed " << strerror(ret) << std::endl;
    }
  }
  ret = pthread_cond_destroy(&condVari);
  if (ret < 0) {
    std::cout << "pthread_cond_destroy failed " << strerror(ret) << std::endl;
  }
  ret = pthread_mutex_destroy(&mutex);
  if (ret < 0) {
    std::cout << "pthread_mutex_destroy failed " << strerror(ret) << std::endl;
  }
  pthread_exit(nullptr);
  srand(time(NULL));
  return 0;
}

void *producer(void *argv) {
  int ret;
  long j = long(argv);
  while (1) {
    int i = rand();
    printf("produce %d:%d\n", j, i);

    ret = pthread_mutex_lock(&mutex);
    if (ret < 0) {
      std::cout << "pthread_mutex_lock failed " << strerror(ret) << std::endl;
    }
    head.push_back(i);
    ret = pthread_mutex_unlock(&mutex);

    if (ret < 0) {
      std::cout << "pthread_mutex_unlock failed " << strerror(ret) << std::endl;
    }
    ret = pthread_cond_signal(&condVari);
    if (ret < 0) {
      std::cout << "pthread_cond_signal failed " << strerror(ret) << std::endl;
    }
    sleep(rand() % 2);
  }
  pthread_exit(nullptr);
  return nullptr;
}

void *consumer(void *argv) {
  int ret;
  long j = long(argv);
  while (1) {
    ret = pthread_mutex_lock(&mutex);
    if (ret < 0) {
      std::cout << "pthread_mutex_lock failed " << strerror(ret) << std::endl;
    }
    while (head.empty()) { // 唤醒后 重新检查是否被其他消费者消费掉了；
      ret = pthread_cond_wait(&condVari, &mutex); //解锁，并阻塞等待条件变量；
      if (ret < 0) {
        std::cout << "pthread_cond_wait failed " << strerror(ret) << std::endl;
      }
    }
    // 注意这里唤醒后，会自动获取互斥锁；
    printf("consume %d:%d\n", j, *(head.end() - 1));
    head.pop_back();
    ret = pthread_mutex_unlock(&mutex);
    if (ret < 0) {
      std::cout << "pthread_mutex_unlock failed " << strerror(ret) << std::endl;
    }
    sleep(rand() % 2);
  }
  pthread_exit(nullptr);
  return nullptr;
}